package com.agentframework.examples.jinhua;

import com.agentframework.core.agent.Agent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author wangjun
 **/
public class JinhuaGameState {

    protected static final Logger logger = LoggerFactory.getLogger(JinhuaGameState.class);

    // 保持加入顺序
    private final Map<String, PlayerInfo> players = new LinkedHashMap<>();

    private final ConcurrentHashMap<Integer, List<PlayerInfo>> roundSnapshots = new ConcurrentHashMap<>();

    private final JinhuaDealerAgent dealerAgent;

    private final List<Agent> playerAgents;

    private final List<String> deck = new ArrayList<>();

    private final int minBet = 5; // 最低蒙牌下注

    private int currentRound = 1;

    private final int totalRounds;

    public JinhuaGameState(JinhuaEnvironment jinhuaEnvironment) {
        jinhuaEnvironment.initPlayers();

        playerAgents = jinhuaEnvironment.getAgents().stream()
                .filter(a -> !(a instanceof JinhuaDealerAgent))
                .toList();

        dealerAgent = (JinhuaDealerAgent) jinhuaEnvironment.getAgents().stream()
                .filter(a -> a instanceof JinhuaDealerAgent)
                .findFirst()
                .orElseThrow();;

        List<String> playerIds = playerAgents.stream().map(Agent::getAgentId).toList();

        for (String id : playerIds) {
            players.put(id, new PlayerInfo(id));
        }
        this.totalRounds = jinhuaEnvironment.getRound();
        shuffleDeck();
    }

    public void shuffleDeck() {
        deck.clear();
        String[] suits = {"♠", "♥", "♣", "♦"};
        String[] ranks = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};
        for (String suit : suits) {
            for (String rank : ranks) {
                deck.add(rank + suit);
            }
        }
        Collections.shuffle(deck);
    }

    public String dealCards() {
        shuffleDeck();
        for (PlayerInfo player : players.values()) {
            player.setCards(new ArrayList<>());
        }
        // 轮流发牌
        for (int i = 0; i < 3; i++) {
            for (PlayerInfo player : players.values()) {
                if (deck.isEmpty()) throw new IllegalStateException("牌堆不足！");
                player.getCards().add(deck.remove(0));
            }
        }
        return "发牌成功，轮到玩家操作。";
    }

    public List<String> viewCards(String playerId) {
        PlayerInfo player = getPlayer(playerId);
        player.setViewed(true);
        return player.getCards();
    }

    public String placeBet(String playerId, int amount) {
        PlayerInfo player = getPlayer(playerId);
        if (!player.isActive()) {
            return playerId + " 已经淘汰，无法下注。";
        }
        int requiredAmount = player.isViewed() ? minBet * 2 : minBet;
        if (amount < requiredAmount) {
            return "下注金额不足，至少需要：" + requiredAmount + "。";
        }
        if (player.getBalance() < amount) {
            return "余额不足，无法下注。请选择弃牌。";
        }
        player.setBalance(player.getBalance() - amount);
        player.setCurrentBet(player.getCurrentBet() + amount);
        return playerId + " 成功下注 " + amount + "，剩余余额：" + player.getBalance();
    }

    public String playerFold(String playerId) {
        PlayerInfo player = getPlayer(playerId);
        player.setActive(false);
        return playerId + " 选择弃牌，退出本轮。";
    }

    public String compareCards(String player1Id, String player2Id) {
        PlayerInfo p1 = getPlayer(player1Id);
        if(!p1.isActive()) {
            return String.format("玩家%s已经被淘汰，不能参与比牌", p1.getPlayerId());
        }
        PlayerInfo p2 = getPlayer(player2Id);
        if(!p2.isActive()) {
            return String.format("玩家%s已经被淘汰，不能参与比牌", p2.getPlayerId());
        }
        int result = JinHuaComparator.compareHands(p1.getCards(), p2.getCards());
        String winner = result > 0 ? player1Id : player2Id;
        String loser = result > 0 ? player2Id : player1Id;

        if (result == 0) {
            // 牌型相同的情况下，比牌者输，被比牌者赢
            loser = player1Id;
        }

        // 输家下线
        players.get(loser).setActive(false);

        return "比牌完成，胜者是：" + winner;
    }

    public boolean nextRound() {
        currentRound++;
        for (PlayerInfo player : players.values()) {
            player.setCurrentBet(0);
            player.setViewed(false);
            player.setActive(true);
        }
        return currentRound <= totalRounds;
    }

    public void saveRoundSnapshot() {
        // 生成状态快照
        List<PlayerInfo> snapshot = getPlayerList().stream()
            .map(p -> new PlayerInfo(p.getPlayerId()) {{
                setCards(new ArrayList<>(p.getCards()));
                setCurrentBet(p.getCurrentBet());
                setActive(p.isActive());
            }})
            .collect(Collectors.toList());

        snapshot.forEach(this::calculatePlayerResults);

        roundSnapshots.put(getCurrentRound(), snapshot);
    }

    private void calculatePlayerResults(PlayerInfo player) {
        if (player.isActive()) {
            // 赢家
            int sum = getPlayerList().stream().filter(p -> !p.isActive()).mapToInt(PlayerInfo::getCurrentBet).sum();
            player.setTotalWinLoss("+" + sum);
        } else {
            // 输家
            PlayerInfo playerInfo = players.get(player.getPlayerId());
            player.setTotalWinLoss("-" + playerInfo.getCurrentBet());
        }
    }

    public void exportGameReport() {

        Path reportDir = Paths.get("..", "data", "jinhua");
        try {
            Files.createDirectories(reportDir);
        } catch (IOException e) {
            logger.error(e.getMessage());
            return;
        }
        Path reportFile = reportDir.resolve("game_report_" + System.currentTimeMillis() + ".csv");

        try (BufferedWriter writer = Files.newBufferedWriter(reportFile)) {
            writer.write("轮次,玩家ID,手牌,当轮下注,总输赢,是否本轮赢家\n");

            roundSnapshots.forEach((round, players) -> players.forEach(p -> {
                try {
                    writer.write(String.format("%d,%s,%s,%d,%s,%s\n",
                            round,
                            p.getPlayerId(),
                            String.join(" ", p.getCards()),
                            p.getCurrentBet(),
                            p.getTotalWinLoss(),
                            p.isActive() ? "是" : "否"));
                } catch (IOException e) {
                    logger.error("写入玩家数据失败", e);
                }
            }));

            // 添加汇总数据
            int totalBets = roundSnapshots.values().stream()
                    .flatMap(List::stream)
                    .mapToInt(PlayerInfo::getCurrentBet)
                    .sum();

            writer.write(String.format("\n总轮次,%d", roundSnapshots.size()));
            writer.write(String.format("\n总下注金额,%d", totalBets));

        } catch (IOException e) {
            logger.error("生成游戏报告失败", e);
        }
    }

    public List<PlayerInfo> getPlayerList() {
        return players.values().stream().toList();
    }

    public PlayerInfo getPlayer(String playerId) {
        if (!players.containsKey(playerId)) {
            throw new IllegalArgumentException("找不到玩家：" + playerId);
        }
        return players.get(playerId);
    }

    public boolean isGameOver() {
        return currentRound > totalRounds;
    }

    /**
     * 只剩下一个玩家存活的时候，代表本轮结束
     * @return true | false
     */
    public boolean isRoundOver() {
        return getPlayerList().stream().filter(PlayerInfo::isActive).count() == 1;
    }

    public JinhuaDealerAgent getDealerAgent() {
        return dealerAgent;
    }

    public List<Agent> getPlayerAgents() {
        return playerAgents;
    }

    public int getCurrentRound() {
        return currentRound;
    }

    public int getTotalRounds() {
        return totalRounds;
    }

    @Override
    public String toString() {
        return "JinhuaGameState{" +
                "players=" + players.values() +
//                ", deck=" + deck +
                ", minBet=" + minBet +
                ", currentRound=" + currentRound +
                ", totalRounds=" + totalRounds +
                '}';
    }

}
